package com.nh.glory.ware.security.authentication.jwt;

import com.nh.glory.core.constant.Constants;
import com.nh.glory.ware.security.authentication.StatelessAuthenticationToken;
import com.nh.glory.ware.security.model.LoginUserDetails;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    private final AuthenticationFailureHandler failureHandler;
    private final JwtFactory jwtFactory;

    public JwtAuthenticationProcessingFilter(RequestMatcher matcher, JwtFactory jsonWebTokenFactory, AuthenticationFailureHandler failureHandler) {
        super(matcher);
        this.failureHandler = failureHandler;
        this.jwtFactory = jsonWebTokenFactory;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        String tokenPayload = request.getHeader(Constants.AUTHORIZATION_HEADER);
        if (StringUtils.isEmpty(tokenPayload)) {
            throw new AuthenticationServiceException("Authorization failed, authorization header cannot be blank");
        }
        if (!tokenPayload.startsWith(Constants.AUTHORIZATION_BEARER_PREFIX)) {
            throw new AuthenticationServiceException("Authorization failed, invalid authorization type");
        }

        JwtToken token = new JwtToken(tokenPayload.substring(Constants.AUTHORIZATION_BEARER_PREFIX.length()));
        if (!jwtFactory.validateToken(token)) {
            throw new BadCredentialsException("Authorization failed, invalid token with payload");
        }
        return getAuthenticationManager().authenticate(new StatelessAuthenticationToken(token));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication)
            throws IOException, ServletException {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        SecurityContextHolder.setContext(context);

        LoginUserDetails principal = (LoginUserDetails) authentication.getPrincipal();
        JwtToken token = jwtFactory.createToken(principal);

        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.addHeader(Constants.AUTHORIZATION_HEADER, Constants.AUTHORIZATION_BEARER_PREFIX + token.getToken());
        chain.doFilter(request, response);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();
        failureHandler.onAuthenticationFailure(request, response, failed);
    }

}
